import asyncio
import time
import os
import sys
import logging

if sys.platform == 'win32':
    import msvcrt
else:
    import fcntl


logger = logging.getLogger(__name__)


class FSLock:
    def __init__(self, dirpath: str, timeout: int = 10000, interval: int = 500) -> None:
        self.timeout = timeout * 0.001
        self.interval = interval * 0.001
        self.locked = False
        self._filename = os.path.join(dirpath, 'dir.lock')

    async def __aenter__(self):
        logger.debug("Attempting to lock %s", self._filename)
        self._fo = open(self._filename, 'w')

        start = time.time()

        while True:
            try:
                if sys.platform == 'win32':
                    # We are locking here fixed location in file to use it as
                    # an exclusive lock on entire file.
                    msvcrt.locking(self._fo.fileno(), msvcrt.LK_NBLCK, 1)
                else:
                    fcntl.flock(self._fo.fileno(), fcntl.LOCK_EX)
                logger.debug("Succeeded to lock %s", self._filename)
                return self._fo
            except OSError:
                if time.time() > start + self.timeout:

                    self._fo.close()
                    try:
                        os.remove(self._filename)
                    except:
                        pass
                    logger.debug("Failed to lock %s", self._filename)
                    raise
                else:
                    await asyncio.sleep(self.interval)

    async def __aexit__(self, exc_type, exc_value, traceback):
        self._fo.flush()

        try:
            if sys.platform == 'win32':
                msvcrt.locking(self._fo.fileno(), msvcrt.LK_UNLCK, 1)
            else:
                fcntl.flock(self._fo.fileno(), fcntl.LOCK_UN)
        finally:
            logger.debug("Unlocking %s", self._filename)
            self._fo.close()
            # os.remove(self._filename)

        return exc_value is None
